#include "selecteffect.h"
#include "transformableactor.h"
#include <osg\Geometry>
#include "utility.h"
#include <osg\ComputeBoundsVisitor>

static const char select_center_vert[] =
	"varying vec3 Position;\n"
	"void main(void) {\n"
		"Position = gl_Vertex.xyz;\n"
		"gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
	"}\n";

static const char select_center_frag[] =
	"varying vec3 Position;\n"
	"uniform float BoxBottom;\n"
	"uniform float BoxHeight;\n"
	"uniform float FrameState;\n"
	"const vec4 SelectedColor0 = vec4 (0.0, 0.0, 1.0, 1.0);\n"
	"const vec4 SelectedColor1 = vec4 (0.0, 0.0, 1.0, 0.0);\n"
	"void main(void)\n"
	"{\n"
		"if (FrameState < 12.0)\n"
		"{\n"
			"float separator = BoxBottom + BoxHeight*(1 - FrameState/10.0);\n"
			"float scale;\n"
			"if (separator > Position.z)\n"
				"scale = 0;\n"
			"else\n"
				"scale = 1-(Position.z - separator)/BoxHeight;\n"
			"scale = clamp(scale, 0.0, 1.0);\n"
			"scale = pow(scale,8);\n"
			"gl_FragColor = mix(SelectedColor1, SelectedColor0, scale);\n"
		"}\n"
		"else\n"
		"{\n"
			"gl_FragColor = SelectedColor1;\n"
		"}\n"
	"}\n";

static const char select_bottom_vert[] =
	"varying vec3 Position;\n"
	"uniform float FrameState;\n"
	"void main(void) {\n"
		"Position = gl_Vertex.xyz;\n"
		"gl_Vertex.x = Position.x + Position.x*sin(FrameState*0.1)*0.1;\n"
		"gl_Vertex.y = Position.y + Position.y*sin(FrameState*0.1)*0.1;\n"
		"gl_Vertex.z = Position.z + Position.z*sin(FrameState*0.1)*0.1;\n"
		"gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
	"}\n";

static const char select_bottom_frag[] =
	"varying vec3 Position;\n"
	"uniform vec3 Center;\n"
	"uniform float Radious;\n"
	"const vec4 SelectedColor0 = vec4 (0.0, 0.0, 1.0, 1.0);\n"
	"const vec4 SelectedColor1 = vec4 (0.0, 0.0, 1.0, 0.0);\n"
	"void main (void)\n"
	"{\n"
		"float dis = length(Position.xy);\n"
		"float scale;\n"
		"scale = dis/Radious;\n"
		"scale = pow(scale,5);\n"
		"gl_FragColor = mix(SelectedColor1, SelectedColor0, scale);\n"
	"}\n";

namespace VrDataCore
{
	SelectEffect::SelectEffect()
	{
		_materialValue = osg::Vec4(0.0f,0.0f,1.0f,0.6f);
	}

	SelectEffect::~SelectEffect()
	{}

	void SelectEffect::buildPropertyMap()
	{}

	void SelectEffect::setMaterial(osg::Vec4 offset)
	{
		_materialValue = offset;
		if(_bindingMeshDrawableActor!=NULL && !isIdle())
		{
			unApply(_bindingMeshDrawableActor);
			apply(dynamic_cast<TransformableActor*>(_bindingMeshDrawableActor));
		}
	}

	osg::Vec4 SelectEffect::getMaterial()
	{
		return _materialValue;
	}

	void SelectEffect::apply( CoreBase::ActorBase* actor)
	{
		if( !isIdle() )
			return ;
		EffectBase::apply(actor);
		osg::Vec3 trans;
		osg::Vec3 rota;
		TransformableActor* transactor = dynamic_cast<TransformableActor*>(actor);
		if(transactor!=NULL)
		{
			trans = transactor->getTranslation();
			rota = transactor->getRotation();
			transactor->setTranslation(osg::Vec3(0,0,0));
			transactor->setRotation(osg::Vec3(0,0,0));
		}
		osg::Node* actornode = actor->getDrawable()->getOSGNode();
		if(transactor!=NULL)
		{
			transactor->setTranslation(trans);
			transactor->setRotation(rota);
		}
		osg::BoundingBox planebb;
		osg::ref_ptr<osg::ComputeBoundsVisitor> boundsVistor = new osg::ComputeBoundsVisitor();
		boundsVistor->reset() ;
		actornode->accept(*boundsVistor.get());
		planebb = boundsVistor->getBoundingBox();
		float radius = (osg::Vec2(planebb.xMax(),planebb.yMax()) - osg::Vec2(planebb.xMin(),planebb.yMin())).length()/2.0f;

		_drawable = new CoreBase::CommonCylinder(this);
		float height = planebb.zMax() - planebb.zMin() + 40;
		_drawable->setCylinderSetting(radius,60,osg::Vec3(0,0,0),
			height,osg::Vec4(1.0f,0.0f,0.0f,1.0f),false,true,true);
		if(!_uniformCenterCallback.valid())
		{
			_uniformCenterCallback = new SelectEffectCallback;
		}
		_uniformCenterCallback->reset();
		if(!_uniformBottomCallback.valid())
		{
			_uniformBottomCallback = new SelectEffectCallback;
		}
		_uniformBottomCallback->reset();
		osg::ref_ptr<osg::StateSet> centerss = _drawable->getOrCreateCenterStateSet();
		osg::ref_ptr<osg::StateSet> bottomss = _drawable->getOrCreateBottomStateSet();
		centerss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
		centerss->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
		
		centerss->setMode(GL_BLEND, osg::StateAttribute::ON);
		centerss->setRenderBinDetails(20, "DepthSortedBin");
		bottomss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
		//bottomss->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
		bottomss->setMode(GL_BLEND, osg::StateAttribute::ON);
		bottomss->setRenderBinDetails(20, "DepthSortedBin");
		osg::ref_ptr<osg::Program> cprogram = new osg::Program;
		cprogram->setName("selecteffect_center program");
		osg::Shader *cVert = new osg::Shader(osg::Shader::VERTEX,select_center_vert);
		osg::Shader *cFrag = new osg::Shader(osg::Shader::FRAGMENT,select_center_frag);
		cprogram->addShader(cVert);
		cprogram->addShader(cFrag);
		centerss->setAttributeAndModes( cprogram.get(), osg::StateAttribute::ON |osg::StateAttribute::PROTECTED);
		osg::ref_ptr<osg::Program> bprogram = new osg::Program;
		bprogram->setName("selecteffect_bottom program");
		osg::Shader *bVert = new osg::Shader(osg::Shader::VERTEX,select_bottom_vert);
		osg::Shader *bFrag = new osg::Shader(osg::Shader::FRAGMENT,select_bottom_frag);
		bprogram->addShader(bVert);
		bprogram->addShader(bFrag);
		bottomss->setAttributeAndModes( bprogram.get(), osg::StateAttribute::ON |osg::StateAttribute::PROTECTED);
		osg::Uniform* boxbottomuniform = new osg::Uniform("BoxBottom", planebb.zMin());
		osg::Uniform* boxheightuniform = new osg::Uniform("BoxHeight", height);
		osg::Uniform* framestateuniform = new osg::Uniform("FrameState",0.0f);
		framestateuniform->setUpdateCallback(_uniformCenterCallback);
		centerss->addUniform(boxbottomuniform);
		centerss->addUniform(boxheightuniform);
		centerss->addUniform(framestateuniform);
		osg::Uniform* bottomcenteruniform = new osg::Uniform("Center", osg::Vec3(planebb.center().x(),planebb.center().y(),planebb.zMin()));
		osg::Uniform* bottomradiousuniform = new osg::Uniform("Radious", radius);
		osg::Uniform* bottomframeuniform = new osg::Uniform("FrameState",0.0f);
		bottomframeuniform->setUpdateCallback(_uniformBottomCallback);
		bottomss->addUniform(bottomcenteruniform);
		bottomss->addUniform(bottomradiousuniform);
		bottomss->addUniform(bottomframeuniform);
		actor->getDrawable()->getOSGNode()->asGroup()->addChild(_drawable->getOSGNode());
	}

	void SelectEffect::unApply(CoreBase::ActorBase* actor)
	{
		actor->getDrawable()->getOSGNode()->asGroup()->removeChild(_drawable->getOSGNode());
		EffectBase::unApply(actor);
	}
}